package gnss_plugs

import (
	"dgo/goutils/gnss"
	"dgo/goutils/utils"
	"fmt"
	"math"
	"os"
	"sort"
	"strings"
	"time"
)

type rinex2x_sign_code struct {
	code        string
	signrinexid byte
}

type rinex2x_obs_rec struct {
	sys byte
	prn string

	//  按照信号索引进行存储
	PseV    [9]float64
	L       [9]float64 // 载波 /* carrier-phase (cycle) */
	Doppler [9]float64 //  /* doppler (hz) */
	Snr     [9]float64
}

type GnssRinexObs2xWriter struct {
	Id               string
	Sys              byte
	bodyCnt          int
	curTmpFileName   string
	lastFileName     string
	Filefmt          string
	tmpPath          string
	SignIsOK         bool
	AutowireSign     bool // 自动更新sign
	signHeadstr      string
	signupflag       bool
	sign_header_code []*rinex2x_sign_code
	signstartT       time.Time
	startTime        time.Time
	currTime         time.Time // 当前时间的观测值
	FixInterval      int64     // 固定采样间隔 单位：秒  5s,  mod(t, 5) = 0

	// 存放一个历元的观测值
	obsList *gnss.ObsEpoch
}

func NewGnssRinexObs2xWriter() *GnssRinexObs2xWriter {
	rval := &GnssRinexObs2xWriter{
		Id:               utils.RandKeyString(4),
		Filefmt:          "",
		currTime:         time.Time{},
		sign_header_code: make([]*rinex2x_sign_code, 0, 256),
		obsList:          gnss.NewObsEpoch(),
	}
	return rval
}

func (this *GnssRinexObs2xWriter) checkNewFile(curT time.Time) bool {
	str := this.Filefmt
	str = utils.ParseTimeFmt(str, curT)
	return strings.Compare(this.lastFileName, str) != 0
}

func (this *GnssRinexObs2xWriter) checkFileStart() {
	if len(this.curTmpFileName) == 0 {
		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}
		strFileN := utils.ParseTimeFmt(this.Filefmt, this.startTime)
		this.lastFileName = strFileN
		this.curTmpFileName = TempFilesDirectory + utils.PathSeparator + "~obs." + utils.RandKeyString(5) + ".tmp"
		utils.ForceCreateFilePath(this.curTmpFileName)
		utils.ForceCreateFilePath(this.lastFileName)
	}
}

func (this *GnssRinexObs2xWriter) checkFileEnd() {
	if len(this.curTmpFileName) == 0 {
		return
	}

	// first line
	str := FPrintRinexV(2.11, 2, 9) + FPrintRinexStrV("OBSERVATION DATA", 27) + FPrintRinexStrV("M (MIXED)", 13)
	str = utils.AddSuffixForWidth(str, 60, " ")
	str += "RINEX VERSION / TYPE\n"

	if this.startTime.IsZero() {
		this.startTime = time.Now()
	}

	str += utils.AddSuffixForWidth(RINEX_TransVer, 20, " ") +
		utils.AddSuffixForWidth(RINEX_CopyRight, 20, " ") +
		utils.AddSuffixForWidth(this.startTime.Format("20060102 150405")+" UTC", 20, " ") +
		"PGM / RUN BY / DATE\n"

	str += this.signHeadstr

	str += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"
	strName := utils.ParseTimeFmt(this.Filefmt, this.startTime)
	utils.NewFileData(strName, []byte(str))
	//utils.InsertBeforeLineFile()
	utils.AppendAllFile(strName, this.curTmpFileName)
	os.Remove(this.curTmpFileName)
	this.curTmpFileName = ""
	this.startTime = utils.ZeroTime
	this.bodyCnt = 0
}

func (this *GnssRinexObs2xWriter) OnRecvGnssRecCallBack(sender interface{}, channelid string, subscribeid interface{}, data interface{}, tag interface{}, isBreak *bool) bool {
	if data == nil {
		this.checkFileEnd()
		return true
	}
	rtcmRec, ok := data.(*gnss.GnssRtcmData)
	if ok {
		this.OnRecvGnssDataRec(sender, rtcmRec)
	}
	return true
}

/*
*
根据signrinexcode数组 自动处理 头的信号字符串
*/
func (this *GnssRinexObs2xWriter) checkSignHeadStr() bool {
	strs := make([]string, 0, 32)
	if len(this.signHeadstr) == 0 {
		for _, v := range this.sign_header_code {
			if v.signrinexid == 1 {
				strs = append(strs, "C1", "P1", "L1", "D1", "S1")
			} else if v.signrinexid == 2 {
				strs = append(strs, "C2", "P2", "L2", "D2", "S2")
			} else {
				n := v.signrinexid
				strs = append(strs, fmt.Sprintf("C%d", n), fmt.Sprintf("L%d", n), fmt.Sprintf("D%d", n), fmt.Sprintf("S%d", n))
			}
		}
		str := FPrintRinexStrV(fmt.Sprintf("%d", len(strs)), 6)
		for n, s := range strs {
			if n > 2 {
				if (n+1)%9 == 1 {
					str += FPrintRinexStrV("", 6) // 第二行开始需要添加6个空格
				}
			}

			str += FPrintRinexStrV(s, 6)
			if n > 2 {
				if (n+1)%9 == 0 {
					str += "# / TYPES OF OBSERV\n"
				}
			}
		}

		// 处理最后一行
		n := len(strs)
		n = 9 - (n % 9)
		if n > 0 {
			str += FPrintRinexStrV("", n*6) + "# / TYPES OF OBSERV\n"
		}

		this.signHeadstr = str
		return true
	}

	return false
}

/**
 * 添加成功返回true, 已经存在返回false
 */
func (this *GnssRinexObs2xWriter) CheckAddSign(sys byte, signv byte) bool {
	rinex2xid := gnss.Sign2Rinex2xID(sys, signv)
	for _, v := range this.sign_header_code {
		if v.signrinexid == rinex2xid {
			return false
		}
	}

	v := &rinex2x_sign_code{
		signrinexid: rinex2xid, // 1-8
	}
	this.sign_header_code = append(this.sign_header_code, v)
	return true
}

func (this *GnssRinexObs2xWriter) checkSignUpdate(rtcmTime time.Time, msm *gnss.RtcmMsm) bool {
	this.signupflag = false
	if this.SignIsOK {
		return true
	}
	if this.signstartT.IsZero() {
		this.signstartT = rtcmTime
	}

	if this.AutowireSign || utils.SecondOf(this.signstartT, rtcmTime) < 30 {
		h := msm.Header
		up := false
		var k byte
		for k = 0; k < h.SigN; k++ {
			sigi := h.Sigs[k]
			if this.CheckAddSign(msm.Sys, sigi) {
				up = true
			}
		}

		if up {
			sort.Slice(this.sign_header_code, func(i, j int) bool {
				itm_i := this.sign_header_code[i]
				itm_j := this.sign_header_code[j]
				return itm_i.signrinexid < itm_j.signrinexid
			})
			this.signupflag = true
			this.signHeadstr = ""
		}
	}

	return utils.SecondOf(this.signstartT, rtcmTime) >= 30
}

/***
 * 是否需要新行
 */
func checkRinex2_NeedNewLine(allstr, newline *string, addstr string) {
	if len(*newline)+len(addstr) > 80 {
		tmpstr := *allstr
		tmpstr += *newline + "\n"
		*allstr = tmpstr
		*newline = addstr
	} else {
		*newline = *newline + addstr
	}
}

/**
 * 写入前一秒的观测值到文件
 */
func (this *GnssRinexObs2xWriter) writePreRinexObsEpoch() {
	if this.bodyCnt == 0 {
		return
	}

	defer func() {
		this.bodyCnt = 0
	}()

	if this.FixInterval > 0 {
		f_sec := float64(this.currTime.UnixNano()) / 1e9 // 秒，有小数
		i_sec := int64(math.Floor(f_sec + 0.5))
		v := i_sec % this.FixInterval
		if v != 0 {
			utils.Log.Debugf("固定采样率为:%d,最后时间:%s", this.FixInterval, utils.DateTimeString(this.currTime))
			return
		}
	}

	// 整理记录
	prnObs := make([]*rinex2x_obs_rec, 0, 256)
	for _, msmlst := range this.obsList.ObsMap {
		for _, v := range msmlst {
			for i := byte(0); i < v.Header.SatN; i++ {
				prnId := v.Header.Sats[i]
				strPrn := gnss.GetPrnStrCode(prnId, v.Sys)
				itm := &rinex2x_obs_rec{
					sys: v.Sys,
					prn: strPrn,
				}
				prnObs = append(prnObs, itm)
				obsv := v.ObsV[i]

				// 循环信号
				for j := byte(0); j < v.Header.SigN; j++ {
					signv := v.Header.Sigs[j]
					rinex2x_sign_id := gnss.Sign2Rinex2xID(v.Sys, signv)
					itm.PseV[rinex2x_sign_id] = obsv.PseV[j]
					itm.L[rinex2x_sign_id] = obsv.L[j]
					itm.Doppler[rinex2x_sign_id] = obsv.Doppler[j]
					itm.Snr[rinex2x_sign_id] = obsv.Snr[j]
				}
			}
		}
	}

	sort.Slice(prnObs, func(i, j int) bool {
		itmi := prnObs[i]
		itmj := prnObs[j]
		r := strings.Compare(itmi.prn, itmj.prn)
		return r < 0
	})

	// 处理历元的头部信息
	strHeader := this.currTime.Format(" 06 01 02 15 04 05.0000000") + "  0"
	strHeader += FPrintRinexStrV(fmt.Sprintf("%d", len(prnObs)), 3)

	strObs := ""
	for n, v := range prnObs {
		if n > 1 && ((n+1)%12 == 1) {
			strHeader += utils.AddSuffixForWidth("", 32, " ")
		}
		strHeader += v.prn
		if n > 1 && ((n+1)%12 == 0) {
			strHeader += "\n"
		}

		//
		var strTmp = ""
		for _, signv := range this.sign_header_code {
			obsv := v.PseV[signv.signrinexid] // C
			if obsv != 0 {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexV(obsv, 3, 14))
			} else {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexStrV("", 14))
			}

			if signv.signrinexid == 1 || signv.signrinexid == 2 { // P
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexStrV("", 14))
			}

			obsv = v.L[signv.signrinexid] // L
			if obsv != 0 {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexV(obsv, 3, 14))
			} else {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexStrV("", 14))
			}

			obsv = v.Doppler[signv.signrinexid] // D
			if obsv != 0 {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexV(obsv, 3, 14))
			} else {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexStrV("", 14))
			}

			obsv = v.Snr[signv.signrinexid] // SNR
			if obsv != 0 {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexV(obsv, 3, 14))
			} else {
				checkRinex2_NeedNewLine(&strObs, &strTmp, FPrintRinexStrV("", 14))
			}

		}
	}
	strHeader += "\n"

	epochStr := strHeader + strObs + "\n" // 写入文件

	if this.checkNewFile(this.currTime) {
		this.checkFileEnd() // 上个文件处理

		this.startTime = this.currTime

		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}

		// 初始化新文件
		this.checkFileStart()
	}

	str := epochStr
	_, err := utils.AppendFile(this.curTmpFileName, []byte(str))
	if err != nil {
		utils.Log.Errf("写入出现异常:%s, 文件:%s", err.Error(), this.curTmpFileName)
	}

}

func (this *GnssRinexObs2xWriter) OnRecvGnssDataRec(sender interface{}, rec *gnss.GnssRtcmData) {
	if rec.TypeId == gnss.TYPE_OBS {
		if obs, ok := rec.Data.(*gnss.RtcmMsm); ok {
			// 检测是否需要更新信号
			if !this.checkSignUpdate(rec.RtcmTime, obs) {
				return
			}

			if this.checkSignHeadStr() {
				// fmt.Println(this.signHeadstr)
			}

			obs.ProcObs()
			if rec.RtcmTime != this.currTime {
				this.writePreRinexObsEpoch()
				this.obsList.Reset()
				this.currTime = rec.RtcmTime
				this.obsList.AddObs(obs)
				this.bodyCnt++
			} else if obs.Header.Sync == 0 {
				this.obsList.AddObs(obs)
				this.bodyCnt++
				this.writePreRinexObsEpoch()
				this.obsList.Reset()
			} else {
				this.obsList.AddObs(obs)
				this.bodyCnt++
			}

		}
	}

}
